package baseFx.database.query;

import baseFx.common.Action1T;
import baseFx.common.Stream;
import baseFx.common.utils.ClassUtils;
import baseFx.database.*;
import baseFx.database.where.QueryWhere;
import baseFx.database.where.SetWhere;

import java.util.List;

public interface DbEntityQuery extends SqlCore, DbEntityCore {
    <T> T get(QueryWhere<T> where, Integer timeout);

    <T> List<T> gets(QueryWhere<T> where, Integer timeout);

    <T> long count(QueryWhere<T> where, Integer timeout);

    <T> CursorReader<T> cursor(QueryWhere<T> where, int fetchSize, boolean independent, Integer timeout);

    <T> PageData<T> page(QueryWhere<T> where, Integer timeout);

    default <T> CursorReader<T> cursor(QueryWhere<T> where, int fetchSize, boolean independent, boolean forUpdate, Integer timeout) {
        if (forUpdate) {
            where = where.clone(s -> s.set("forUpdate", ForUpdate.Lock));
        }
        return cursor(where, fetchSize, independent, timeout);
    }

    default <T> T get(QueryWhere<T> where, boolean forUpdate, Integer timeout) {
        if (forUpdate) {
            where = where.clone(s -> s.set("forUpdate", ForUpdate.Lock));
        }
        return get(where, timeout);
    }

    default <T> List<T> gets(QueryWhere<T> where, boolean forUpdate, Integer timeout) {
        if (forUpdate) {
            where = where.clone(s -> s.set("forUpdate", ForUpdate.Lock));
        }
        return gets(where, timeout);
    }

    default <T> PageData<T> page(QueryWhere<T> where) {
        return page(where, null);
    }

    default <T> PageData<T> pageBy(Class<T> table, Action1T<SetWhere<T>> action, Integer timeout) {
        SetWhere<T> setWhere = new SetWhere<>(table);
        action.execute(setWhere);
        return page(setWhere.queryWhere(), timeout);
    }

    default <T> PageData<T> pageBy(Class<T> table, Action1T<SetWhere<T>> action) {
        return pageBy(table, action, null);
    }

    default <T> CursorReader<T> cursor(QueryWhere<T> where, boolean independent, boolean forUpdate) {
        return cursor(where, 1, independent, forUpdate, null);
    }

    default <T> CursorReader<T> cursor(QueryWhere<T> where, boolean independent) {
        return cursor(where, independent, false);
    }

    default <T> CursorReader<T> cursor(QueryWhere<T> where, int fetchSize) {
        return cursor(where, fetchSize, false, false, null);
    }

    default <T> CursorReader<T> cursor(QueryWhere<T> where) {
        return cursor(where, false);
    }

    default <T> CursorReader<T> cursor(Class<T> table, T model, int fetchSize, boolean independent, boolean forUpdate, Integer timeout) {
        return cursor(new QueryWhere<T>(table) {
            @Override
            public T eq() {
                return model;
            }
        }, fetchSize, independent, forUpdate, timeout);
    }

    default <T> CursorReader<T> cursor(Class<T> table, T model, boolean independent, boolean forUpdate) {
        return cursor(table, model, 1, independent, forUpdate, null);
    }

    default <T> CursorReader<T> cursor(Class<T> table, T model, boolean independent) {
        return cursor(table, model, independent, false);
    }

    default <T> CursorReader<T> cursor(Class<T> table, T model) {
        return cursor(table, model, false);
    }

    default <T> CursorReader<T> cursor(Class<T> table) {
        return cursor(new QueryWhere<>(table));
    }

    default <T> CursorReader<T> cursor(Class<T> table, int fetchSize) {
        return cursor(new QueryWhere<>(table), fetchSize);
    }

    default <T> CursorReader<T> cursorby(Class<T> table, Action1T<T> action, int fetchSize, boolean independent, boolean forUpdate, Integer timeout) {
        T model = ClassUtils.newInstance(table);
        action.execute(model);
        return cursor(new QueryWhere<T>(table) {
            @Override
            public T eq() {
                return model;
            }

            @Override
            public boolean allowEmptyCondition() {
                return false;
            }
        }, fetchSize, independent, forUpdate, timeout);
    }

    default <T> CursorReader<T> cursorby(Class<T> table, Action1T<T> action, boolean independent, boolean forUpdate) {
        return cursorby(table, action, 1, independent, forUpdate, null);
    }

    default <T> CursorReader<T> cursorby(Class<T> table, Action1T<T> action, int fetchSize) {
        return cursorby(table, action, fetchSize, false, false, null);
    }

    default <T> CursorReader<T> cursorby(Class<T> table, Action1T<T> action, boolean independent) {
        return cursorby(table, action, independent, false);
    }

    default <T> CursorReader<T> cursorby(Class<T> table, Action1T<T> action) {
        return cursorby(table, action, false);
    }

    default <T> CursorReader<T> cursorBy(Class<T> table, Action1T<SetWhere<T>> action, int fetchSize, boolean independent, boolean forUpdate, Integer timeout) {
        SetWhere<T> setWhere = new SetWhere<>(table);
        action.execute(setWhere);
        return cursor(setWhere.queryWhere(), fetchSize, independent, forUpdate, timeout);
    }

    default <T> CursorReader<T> cursorBy(Class<T> table, Action1T<SetWhere<T>> action, int fetchSize, boolean independent) {
        return cursorBy(table, action, fetchSize, independent, false, null);
    }

    default <T> CursorReader<T> cursorBy(Class<T> table, Action1T<SetWhere<T>> action, int fetchSize) {
        return cursorBy(table, action, fetchSize, false);
    }

    default <T> CursorReader<T> cursorBy(Class<T> table, Action1T<SetWhere<T>> action, boolean independent, boolean forUpdate) {
        return cursorBy(table, action, 1, independent, forUpdate, null);
    }

    default <T> CursorReader<T> cursorBy(Class<T> table, Action1T<SetWhere<T>> action, boolean independent) {
        return cursorBy(table, action, independent, false);
    }

    default <T> CursorReader<T> cursorBy(Class<T> table, Action1T<SetWhere<T>> action) {
        return cursorBy(table, action, false);
    }

    default <T> CursorReader<T> cursorBy(Class<T> table, Action1T<SetWhere<T>> action, boolean independent, int fetchSize, boolean forUpdate) {
        SetWhere<T> setWhere = new SetWhere<>(table);
        action.execute(setWhere);
        return cursor(setWhere.queryWhere(), fetchSize, independent, forUpdate, null);
    }

    default <T> T get(QueryWhere<T> where, boolean forUpdate) {
        return get(where, forUpdate, null);
    }

    default <T> T get(QueryWhere<T> where) {
        return get(where, false);
    }

    default <T> T get(Class<T> table, T model, boolean forUpdate, Integer timeout) {
        return get(new QueryWhere<T>(table) {
            @Override
            public T eq() {
                return model;
            }
        }, forUpdate, timeout);
    }

    default <T> T get(Class<T> table, T model, boolean forUpdate) {
        return get(table, model, forUpdate, null);
    }

    default <T> T get(Class<T> table, T model) {
        return get(table, model, false);
    }

    default <T> T get(Class<T> table) {
        return get(new QueryWhere<>(table));
    }

    default <T> T getby(Class<T> table, Action1T<T> action, boolean forUpdate, Integer timeout) {
        T model = ClassUtils.newInstance(table);
        action.execute(model);
        return get(new QueryWhere<T>(table) {
            @Override
            public T eq() {
                return model;
            }

            @Override
            public boolean allowEmptyCondition() {
                return false;
            }
        }, forUpdate, timeout);
    }

    default <T> T getby(Class<T> table, Action1T<T> action, boolean forUpdate) {
        return getby(table, action, forUpdate, null);
    }

    default <T> T getby(Class<T> table, Action1T<T> action) {
        return getby(table, action, false);
    }

    default <T> T getBy(Class<T> table, Action1T<SetWhere<T>> action, boolean forUpdate) {
        SetWhere<T> setWhere = new SetWhere<>(table);
        action.execute(setWhere);
        return get(setWhere.queryWhere(), forUpdate);
    }

    default <T> T getBy(Class<T> table, Action1T<SetWhere<T>> action) {
        return getBy(table, action, false);
    }

    default <T> List<T> gets(QueryWhere<T> where, boolean forUpdate) {
        return gets(where, forUpdate, null);
    }

    default <T> List<T> gets(QueryWhere<T> where) {
        return gets(where, false);
    }

    default <T> List<T> gets(Class<T> table, T model, boolean forUpdate, Integer timeout) {
        return gets(new QueryWhere<T>(table) {
            @Override
            public T eq() {
                return model;
            }
        }, forUpdate, timeout);
    }

    default <T> List<T> gets(Class<T> table, T model, boolean forUpdate) {
        return gets(table, model, forUpdate, null);
    }

    default <T> List<T> gets(Class<T> table, T model) {
        return gets(table, model, false);
    }

    default <T> List<T> gets(Class<T> table) {
        return gets(new QueryWhere<>(table));
    }

    default <T> List<T> getsby(Class<T> table, Action1T<T> action, boolean forUpdate, Integer timeout) {
        T model = ClassUtils.newInstance(table);
        action.execute(model);
        return gets(new QueryWhere<T>(table) {
            @Override
            public T eq() {
                return model;
            }

            @Override
            public boolean allowEmptyCondition() {
                return false;
            }
        }, forUpdate, timeout);
    }

    default <T> List<T> getsby(Class<T> table, Action1T<T> action, boolean forUpdate) {
        return getsby(table, action, forUpdate, null);
    }

    default <T> List<T> getsby(Class<T> table, Action1T<T> action) {
        return getsby(table, action, false);
    }

    default <T> List<T> getsBy(Class<T> table, Action1T<SetWhere<T>> action) {
        return getsBy(table, action, false);
    }

    default <T> List<T> getsBy(Class<T> table, Action1T<SetWhere<T>> action, boolean forUpdate) {
        SetWhere<T> setWhere = new SetWhere<>(table);
        action.execute(setWhere);
        return gets(setWhere.queryWhere(), forUpdate);
    }

    default <T> long count(QueryWhere<T> where) {
        return count(where, null);
    }

    default <T> long count(Class<T> table, T model, Integer timeout) {
        return count(new QueryWhere<T>(table) {
            @Override
            public T eq() {
                return model;
            }
        }, timeout);
    }

    default <T> long count(Class<T> table, T model) {
        return count(table, model, null);
    }

    default <T> long count(Class<T> table) {
        return count(new QueryWhere<>(table));
    }

    default <T> long countby(Class<T> table, Action1T<T> action, Integer timeout) {
        T model = ClassUtils.newInstance(table);
        action.execute(model);
        return count(new QueryWhere<T>(table) {
            @Override
            public T eq() {
                return model;
            }

            @Override
            public boolean allowEmptyCondition() {
                return false;
            }
        });
    }

    default <T> long countby(Class<T> table, Action1T<T> action) {
        return countby(table, action, null);
    }

    default <T> long countBy(Class<T> table, Action1T<SetWhere<T>> action) {
        SetWhere<T> setWhere = new SetWhere<>(table);
        action.execute(setWhere);
        return count(setWhere.queryWhere());
    }

    default <T> Stream<T> streamby(Class<T> table, Action1T<T> action) {
        List<T> list = getsby(table, action);
        return Stream.create(list);
    }

    default <T> Stream<T> streamBy(Class<T> table, Action1T<SetWhere<T>> action) {
        List<T> list = getsBy(table, action);
        return Stream.create(list);
    }
}
